맞춤형 의학연구 애플리케이션을 위한 개발 환경 구축

Jinseob Kim

October 26, 2018

Executive Summary

맞춤형 의학연구 애플리케이션을 위해

  1. Rstudioshiny server가 설치된 Docker 이미지를 만들고 이것을 Docker swarm을 이용해 배포함으로써 서버의 종류와 갯수에 구애받지 않는 마이크로서비스 아키텍처(microservice architecture)를 구축하였으며

  2. 동적 프록시 서버(dynamic proxy server) 프로그램인 Traefik 을 이용하여 서비스가 추가될 때 마다(ex: 홈페이지, Jupyter) 이에 맞추어 https 보안이 적용된 subdomain 주소를 부여하였습니다.

  3. 흔히 이용되는 의학통계 방법들을 ShinyApps 로 만들어 위의 환경에 배포하였으며, 모든 앱에는 논문/보고서용 테이블과 그림을 위해 데이터 라벨(label) 정보를 적용하였습니다.

1. 마이크로서비스 아키텍처

https://blog.philipphauer.de/microservices-nutshell-pros-cons/

  1. Docker 이미지 rocker/studio 활용하여 Rstudio & Shiny-server 컨테이너 제작.

  2. Docker-machine으로 docker가 설치된 클라우드 컴퓨터 생성.

  3. Docker swarm 으로 클러스터링.

  4. 스웜환경에서 Docker image를 서비스로 구동.

Docker

https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html

Docker-machine

base=https://github.com/docker/machine/releases/download/v0.15.0 &&
curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine &&
sudo install /tmp/docker-machine /usr/local/bin/docker-machine
docker-machine version

Manager node

export DIGITALOCEAN_ACCESS_TOKEN=<YOUR_DIGITALOCEAN_ACCESS_TOKEN>
export DIGITALOCEAN_IMAGE="ubuntu-18-04-x64"
export DIGITALOCEAN_REGION="sgp1"
echo "### Creating manager nodes ..."

for c in {1..1} ; do
  docker-machine create \
     --driver digitalocean \
     --digitalocean-access-token $DIGITALOCEAN_ACCESS_TOKEN \
     --digitalocean-image $DIGITALOCEAN_IMAGE \
     --digitalocean-region $DIGITALOCEAN_REGION \
     --digitalocean-size "s-2vcpu-4gb" \
     manager$c &&\
  docker-machine ssh manager$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
done

Worker node

export DIGITALOCEAN_SIZE="s-1vcpu-1gb"
echo "### Creating worker nodes ..."
for c in {1..1} ; do
    docker-machine create \
  --driver digitalocean \
  --digitalocean-access-token $DIGITALOCEAN_ACCESS_TOKEN \
  --digitalocean-image $DIGITALOCEAN_IMAGE \
  --digitalocean-region $DIGITALOCEAN_REGION \
  --digitalocean-size $DIGITALOCEAN_SIZE \
  worker$c && \
  docker-machine ssh worker$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
done

Other cloud: AWS

export AWS_ACCESS_KEY_ID=<YOUR_AWS_ACEESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<YOUR_AWS_SECRET_ACCESS_KEY>
export AWS_INSTANCE_TYPE="t2.micro" 
export AWS_INSTANCE_REGION="ap-northeast-2"
export AWS_SECURITY_GROUP="launch-wizard-2"
export AWS_VPC_ID=<YOUR_AWS_VPC_ID>
export AWS_ZONE=c


for c in {1..1} ; do
docker-machine create \
  --driver amazonec2 \
  --amazonec2-access-key $AWS_ACCESS_KEY_ID \
  --amazonec2-secret-key $AWS_SECRET_ACCESS_KEY \
  --amazonec2-region $AWS_INSTANCE_REGION \
  --amazonec2-vpc-id $AWS_VPC_ID \
  --amazonec2-open-port 3838 \
  --amazonec2-open-port 8787 \
  --amazonec2-open-port 8000 \
  --amazonec2-open-port 8080 \
  --amazonec2-open-port 2377 \
  --amazonec2-open-port 7946 \
  --amazonec2-open-port 7946/udp \
  --amazonec2-open-port 4789 \
  --amazonec2-open-port 4789/udp \
  --amazonec2-open-port 8888 \
  --amazonec2-open-port 80 \
  --amazonec2-open-port 443 \
  manager$c && \
  docker-machine ssh manager$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
done

Other cloud: AZURE

export sub=<YOUR_AZURE_SUBSCRIPTION_VALUE>

for c in {1..1} ; do
docker-machine create \
    --driver azure \
    --azure-location "koreacentral" \
    --azure-size Standard_B1s \
    --azure-subscription-id $sub \
    --azure-open-port 3838 \
    --azure-open-port 8787 \
    --azure-open-port 8000 \
    --azure-open-port 8080 \
    --azure-open-port 2377 \
    --azure-open-port 7946 \
    --azure-open-port 7946/udp \
    --azure-open-port 4789 \
    --azure-open-port 4789/udp \
    --azure-open-port 8888 \
    --azure-open-port 80 \
    --azure-open-port 443 \
    manager$c && \
    docker-machine ssh manager$c "adduser js --gecos 'First Last,RoomNumber,WorkPhone,HomePhone' --disabled-password && sh -c 'echo js:js | sudo chpasswd' && usermod -aG sudo js"
done

Docker swarm

manager1worker1 노드를 docker swarm를 활용하여 묶자.

# Get IP from leader node
leader_ip=$(docker-machine ip manager1)

# Init Docker Swarm mode
echo "### Initializing Swarm mode ..."
eval $(docker-machine env manager1)
docker swarm init --advertise-addr $leader_ip

# Swarm tokens
manager_token=$(docker swarm join-token manager -q)
worker_token=$(docker swarm join-token worker -q)

# Joinig manager nodes
echo "### Joining manager modes ..."
for c in {1..1} ; do
    eval $(docker-machine env manager$c)
    docker swarm join --token $manager_token $leader_ip:2377
done

# Join worker nodes
echo "### Joining worker modes ..."
for c in {1..1} ; do
    eval $(docker-machine env worker$c)
    docker swarm join --token $worker_token $leader_ip:2377
done


# Clean Docker client environment
echo "### Cleaning Docker client environment ..."
eval $(docker-machine env -u)

서비스 실행: rstudio & shiny server

자체적으로 이미지 docker-rshiny 를 만들어 사용하였다.

docker service create \
    --name rshiny \
    --publish 8787:8787 \
    --publish 3838:3838 \ 
    -e USER=js -e PASSWORD=js -e ROOT=TRUE \
    --mount=type=bind,src=/home/js,dst=/home/rstudio \
    --network traefik-net \
     jinseob2kim/docker-rshiny

8787 포트에서 ’rstudio server’를, 3838 포트에서 ’shiny server’를 실행할 수 있다.

2. Dynamic proxy & https: Traefik

eval $(docker-machine env manager1)
DOMAINNAME="anpanman.co.kr"

# Create network for swarm
docker network create --driver=overlay traefik-net

# For Let's Encrypt
docker-machine ssh manager1 "DOMAINNAME=anpanman.co.kr && \ 
                             mkdir /home/js/opt && \ 
                             mkdir /home/js/opt/traefik && \
                             cd /home/js/opt/traefik && \
                             touch acme.json && chmod 600 acme.json && \
                             wget -O traefik.toml  https://raw.githubusercontent.com/jinseob2kim/swarm-setting/master/opt/traefik/traefik.toml"
                             
                             
# Create traefik service
docker service create \
    --name traefik \
    --constraint=node.role==manager \
    --publish 80:80 --publish 443:443\
    --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
    --mount type=bind,source=/root/acme.json,target=/acme.json \
    --mount type=bind,source=/root/traefik.toml,target=/traefik.toml \
    -e DO_AUTH_TOKEN=$DIGITALOCEAN_ACCESS_TOKEN \
    -l traefik.port=8080 \
    -l traefik.frontend.rule=Host:monitor.$DOMAINNAME\
    --network traefik-net \
    traefik \
    --logLevel=INFO \
    --docker \
    --docker.swarmMode \
    --docker.watch \
    --docker.domain=$DOMAINNAME

https://monitor.anpanman.co.kr 에서 dashboard를 볼 수 있다.

서비스 재실행: rstudio & shiny server

Traefik를 적용하여 재실행하자.

docker service create \
    --name rshiny \
    --label traefik.shiny.port=3838 \
    --label traefik.rstudio.port=8787 \
    --label traefik.shiny.frontend.rule="Host:app.anpanman.co.kr" \
    --label traefik.rstudio.frontend.rule="Host:server.anpanman.co.kr" \
    -e USER=js -e PASSWORD=js -e ROOT=TRUE \
    --mount=type=bind,src=/home/js,dst=/home/js \
    --network traefik-net \
     jinseob2kim/docker-rshiny

https://server.anpanman.co.kr 에서 ’rstudio server’를, https://app.anpanman.co.kr 에서 ’shiny server’를 실행할 수 있다.

서비스 추가: 홈페이지

proxy server 프로그램인 nginxdocker image 를 이용하였고, blogdown 패키지 를 활용해서 홈페이지를 만들었다.

docker service create \
    --name nginx \
    --label traefik.port=80 \
    --label traefik.frontend.rule="Host:${DOMAINNAME},www.${DOMAINNAME}" 
    --network traefik-net \
    nginx 

https://anpanman.co.kr, https://www.anpanman.co.kr 에서 nginx 실행환경을 볼 수 있다.

3. 의학연구용 ShinyApps 만들기

Label

Table 1: tableone package

tableone package

Figure

A scatterplot.

A scatterplot.

Anpanman 은 앞으로 맞춤형 의학연구 지원을 통해 의학연구 활성화를 이끌겠습니다. 감사합니다.